package com.glsc.ngateway.opmanage.service;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.glsc.ngateway.common.api.common.enums.DictConstant;
import com.glsc.ngateway.common.api.common.enums.PlatformConstant;
import com.glsc.ngateway.common.base.domain.mysql.gateway.platform.ProcessEditConfig;
import com.glsc.ngateway.common.base.domainmapper.ProcessEditConfigMapper;
import com.glsc.ngateway.common.api.flowable.dto.FlowCommonTaskDto;
//import com.glsc.ngateway.common.api.platform.dto.flow.ProcessEditConfigDto;
import com.glsc.ngateway.common.api.flowable.dto.task.config.ProcessEditConfigDto;
import com.glsc.ngateway.common.api.common.utils.CommonUtils;
import com.glsc.ngateway.opmanage.exception.PlatformException;
import com.glsc.ngateway.opmanage.utils.SpecificationUtil;
import com.glsc.ngateway.common.base.repo.mysql.gateway.platform.ProcessEditConfigRepo;
import com.glsc.ngateway.common.api.common.utils.PageUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.*;

@Service
public class ProcessEditConfigService {
    private static final Logger logger = LoggerFactory.getLogger(ProcessEditConfigService.class);

    @Resource
    private ProcessEditConfigRepo ProcessEditConfigRepo;

//    @Resource
//    private EventManageService eventManageService;

//    @Resource
//    private EventImproveService eventImproveService;

    /**
     * 查询流程节点可编辑字段，如没有相关配置，返回空对象，而不会返回NULL
     */
    public ProcessEditConfigDto getProcessEditConfigDto(String processType, String nodeType) {
        if (StrUtil.isBlank(processType) || StrUtil.isBlank(nodeType)) {
            logger.error("查询流程节点可编辑字段，流程类型标识和流程节点标识不可为空！");
            return new ProcessEditConfigDto();
        }

        ProcessEditConfig processEditConfig = ProcessEditConfigRepo.findByProcessTypeAndNodeTypeAndActivated(processType, nodeType, DictConstant.YES);
        if (processEditConfig == null) {//方便前端开发，不返回空对象
            ProcessEditConfigDto dto = new ProcessEditConfigDto();
            dto.setProcessType(processType);
            dto.setNodeType(nodeType);
            dto.setEditFieldList(new ArrayList<>());
            dto.setOptionalEditFieldList(new ArrayList<>());
            dto.setTaskDescription("");
            return dto;
        }

        ProcessEditConfigDto dto = ProcessEditConfigMapper.INSTANCE.domain2dto(processEditConfig);
        try {
            dto.setEditFieldList(convertEditFields(processEditConfig.getEditFields()));//处理可编辑字段列表中的通配符
            dto.setOptionalEditFieldList(convertEditFields(processEditConfig.getOptionalEditFields()));//处理选填字段列表中的通配符
        } catch (Exception e) {
            throw PlatformException.error(e.getLocalizedMessage());
        }

        //保存dtoStr-编辑字段fields的map
        Map<String, Set<String>> editFieldWithDtoMap = new HashMap<>();
        //记录所有保存字段，最后存入flowableFormData
        Set<String> allFieldSet = new HashSet<>();
        for (String editField : dto.getEditFieldList()) {
            if (StrUtil.isEmpty(editField)) {
                continue;
            }
            editField = StrUtil.trim(editField);

            String[] dtoFieldArray = editField.split("\\.");
            if (dtoFieldArray.length < 2) { //特殊保存，非表字段保存
                continue;
            }
            String dtoStr = dtoFieldArray[0];
            String field = dtoFieldArray[1];
            allFieldSet.add(field);
            if (editFieldWithDtoMap.containsKey(dtoStr)) {
                editFieldWithDtoMap.get(dtoStr).add(field);
            } else {
                Set<String> fieldSet = new HashSet<>();
                fieldSet.add(field);
                editFieldWithDtoMap.put(dtoStr, fieldSet);
            }
        }
        //记录所有保存字段，最后存入flowableFormData
        if (CollectionUtil.isNotEmpty(allFieldSet)) {
            editFieldWithDtoMap.put(PlatformConstant.OBJ_FORM_DATA, allFieldSet);
        }

        dto.setEditFieldWithDtoMap(editFieldWithDtoMap);



        //选填字段
        //保存dtoStr-编辑字段fields的map
        Map<String, Set<String>> optionalEditFieldWithDtoMap = new HashMap<>();
        //记录所有保存字段，最后存入flowableFormData
        Set<String> optionalEditFieldSet = new HashSet<>();
        for (String editField : dto.getEditFieldList()) {
            if (StrUtil.isEmpty(editField)) {
                continue;
            }
            editField = StrUtil.trim(editField);

            String[] dtoFieldArray = editField.split("\\.");
            if (dtoFieldArray.length < 2) { //特殊保存，非表字段保存
                continue;
            }
            String dtoStr = dtoFieldArray[0];
            String field = dtoFieldArray[1];
            optionalEditFieldSet.add(field);
            if (optionalEditFieldWithDtoMap.containsKey(dtoStr)) {
                optionalEditFieldWithDtoMap.get(dtoStr).add(field);
            } else {
                Set<String> fieldSet = new HashSet<>();
                fieldSet.add(field);
                optionalEditFieldWithDtoMap.put(dtoStr, fieldSet);
            }
        }
        //记录所有保存字段，最后存入flowableFormData
        if (CollectionUtil.isNotEmpty(optionalEditFieldSet)) {
            editFieldWithDtoMap.put(PlatformConstant.OBJ_FORM_DATA, optionalEditFieldSet);
        }

        dto.setOptionalEditFieldWithDtoMap(optionalEditFieldWithDtoMap);

        return dto;
    }

    /**
     * 转换配置中编辑字段列表的通配符
     * @param editFields 英文逗号分割的多个可编辑字段
     */
    private List<String> convertEditFields(String editFields) throws Exception {
        if (StrUtil.isBlank(editFields)) {
            return new ArrayList<>();
        }
        //保存处理后的字段列表
        List<String> editFieldList = new ArrayList<>();
        FlowCommonTaskDto taskDto = new FlowCommonTaskDto();
        Field[] taskDtoFields = taskDto.getClass().getDeclaredFields();

        String[] editfieldArray = editFields.split(",");
        for (String editField : editfieldArray) {
            //处理通配符
            if (editField.contains("*")) {
                String objName = editField.split("\\.")[0];
                for (Field taskField : taskDtoFields) {
                    if (taskField.getName().equals(objName)) {
                        taskField.setAccessible(true);
                        Class clazz = taskField.get(taskDto).getClass();
                        //获取对应类属性
                        Set<String> fieldNameSet = CommonUtils.getFieldNameList(clazz);
                        fieldNameSet.remove("serialVersionUID");
                        fieldNameSet.remove("opSource");
                        fieldNameSet.remove("opAction");
                        fieldNameSet.remove("opOperator");
                        fieldNameSet.remove("opOperatorName");
                        fieldNameSet.remove("opTime");
                        fieldNameSet.forEach(fieldName -> editFieldList.add(objName + "." + fieldName));
                        break;
                    }
                }
            } else {
                editFieldList.add(StrUtil.trim(editField));
            }
        }
        return editFieldList;
    }


    //获取所有配置项
    public List<ProcessEditConfig> findAllByProcessType(String processType) {
        List<ProcessEditConfig> configList = ProcessEditConfigRepo.findAllByProcessTypeAndActivated(processType, DictConstant.YES);
        if (configList == null) {
            configList = new ArrayList<>();
        }

        return configList;
    }


    /**
     * 保存产品要素
     *
     * @param processId           流程ID
     * @param prodUniqueId        产品唯一编码ID
     * @param editFieldWithDtoMap 保存的对象类型及属性Map 格式如：productBriefDto:registerNo,registerConfirmDate
     * @param saveFlowFormDto     保存的属性对象Dto
     */
    public void saveFieldsToProductElementByConfig(String processId, String prodUniqueId, Map<String, Set<String>> editFieldWithDtoMap, Object saveFlowFormDto) {
        logger.info("流程通用要素保存{}={}，字段{}，保存对象数据={}", processId, prodUniqueId, editFieldWithDtoMap, saveFlowFormDto);
        if (StrUtil.isEmpty(processId) || StrUtil.isEmpty(prodUniqueId) || MapUtil.isEmpty(editFieldWithDtoMap) || saveFlowFormDto == null) {
            logger.error("流程通用要素保存异常，必要信息为空");
            return;
        }

        //按dto类型保存产品各要素表
        String dtoStr = null;
        Set<String> fieldSet = null;
        for (Map.Entry<String, Set<String>> entry : editFieldWithDtoMap.entrySet()) {
            dtoStr = entry.getKey();
            fieldSet = entry.getValue();
            this.saveFieldsToProductElementByConfig(dtoStr, fieldSet, prodUniqueId, processId, saveFlowFormDto);
        }
    }

    /**
     * 保存产品要素
     *
     * @param objName      dto对象名
     * @param fieldSet 需要保存的要素集合
     */
    private void saveFieldsToProductElementByConfig(String objName, Set<String> fieldSet, String prodUniqueId, String processId, Object flowableFormData) {

//        logger.info("流程{}，保存产品={}，字段{}，保存对象数据={}", processId, prodUniqueId, fieldSet.toString(), flowableFormData.toString());
//
//        if (PlatformConstant.OBJ_FORM_DATA.equals(objName) && flowableFormData instanceof FlowFormEvent) { // 事件管理流程
//
//            FlowFormEvent flowForm = eventManageService.findFormByProcessId(processId);
//            if (flowForm == null) {
//                flowForm = new FlowFormEvent();
//            }
//            flowForm.setProcessId(processId);
//            CommonUtils.copyPropertiesOnly(flowableFormData, flowForm, fieldSet);
//            eventManageService.saveOrUpdateForm(flowForm);
//        } else if (PlatformConstant.OBJ_FORM_DATA.equals(objName) && flowableFormData instanceof FlowFormEventImprove) { // 非产品要素数据，流程表单数据存储， 新设流程表单
//
//            FlowFormEventImprove flowForm = eventImproveService.findFormByProcessId(processId);
//            if (flowForm == null) {
//                flowForm = new FlowFormEventImprove();
//            }
//            flowForm.setProcessId(processId);
//            CommonUtils.copyPropertiesOnly(flowableFormData, flowForm, fieldSet);
//            eventImproveService.saveOrUpdateForm(flowForm);
//        }
    }

    /**
     * 分页查询
     * @param pageNo
     * @param pageSize
     * @param param
     * @return
     */
    public Page<ProcessEditConfig> findPage(int pageNo, int pageSize, Map<String, Object> param) {
        Sort.Direction direction = Sort.Direction.ASC;
        String sort = "processType";

        pageNo = PageUtil.initPageNo(pageNo);
        pageSize = PageUtil.initPageSize(pageSize);

        Pageable pageable = PageRequest.of(pageNo - 1, pageSize, direction, sort);
        return ProcessEditConfigRepo.findAll(SpecificationUtil.buildSpecification(param, ProcessEditConfig.class), pageable);
    }

    /**
     * 保存
     * @param processEditConfig
     */
    public ProcessEditConfig save(ProcessEditConfig processEditConfig) {
        //TODO 必填检查,已存在的校验通过数据库保证唯一
        checkDataNull(processEditConfig);
        ProcessEditConfig processEditConfigDB = ProcessEditConfigRepo.findByProcessTypeAndNodeTypeAndActivated(processEditConfig.getProcessType(),processEditConfig.getNodeType(), DictConstant.ACTIVATED_YES);
        Assert.isNull(processEditConfigDB,"存在相同的流程类型和流程节点");
        //设置删除状态及操作人员
        processEditConfig.setActivated(DictConstant.ACTIVATED_YES);
//        requestTool.fillOpInfo(processEditConfig, DictConstant.OP_SOURCE_WEB, DictConstant.OP_ACTION_ADD);

        return ProcessEditConfigRepo.save(processEditConfig);
    }

    /**
     * 检查节点不为空
     * @param processEditConfig
     */
    private void checkDataNull(ProcessEditConfig processEditConfig) {
        Assert.notNull(processEditConfig,"保存流程节点可编辑字段表设置信息无效,参数为空");
        Assert.hasLength(processEditConfig.getEditFields(),"保存流程节点可编辑字段表设置信息无效,节点可以编辑字段列表为空");
        Assert.hasLength(processEditConfig.getProcessType(),"保存流程节点可编辑字段表设置信息无效,流程类型标识为空");
        Assert.hasLength(processEditConfig.getNodeType(),"保存流程节点可编辑字段表设置信息无效,流程节点标识为空");
        Assert.hasLength(processEditConfig.getTaskDescription(),"保存流程节点可编辑字段表设置信息无效,节点任务描述为空");
        Assert.hasLength(processEditConfig.getNodeName(),"保存流程节点可编辑字段表设置信息无效,流程节点中文名称");
    }

    /**
     * 更新
     * @param processEditConfig
     */
    public ProcessEditConfig update(ProcessEditConfig processEditConfig) {
        //TODO 必填检查,已存在的校验通过数据库保证唯一
        checkDataNull(processEditConfig);
        ProcessEditConfig processEditConfigDB = ProcessEditConfigRepo.findByProcessTypeAndNodeTypeAndActivated(processEditConfig.getProcessType(),processEditConfig.getNodeType(), DictConstant.ACTIVATED_YES);
        Assert.notNull(processEditConfigDB,"要更新流程节点可编辑字段表设置信息，根据类型和节点查询不存在");

        processEditConfig.setActivated(DictConstant.ACTIVATED_YES);
//        RequestTool.fillOpInfo(processEditConfig, DictConstant.OP_SOURCE_WEB, DictConstant.OP_ACTION_UPDATE);
        return ProcessEditConfigRepo.save(processEditConfig);
    }

    /**
     * 标记删除
     * @param processType
     * @param nodeType
     */
    public ProcessEditConfig updateDeletedByProcessTypeAndNodeType(String processType, String nodeType) {
        Assert.hasLength(processType,"流程类型标识为空");
        Assert.hasLength(nodeType,"流程节点标识为空");

        ProcessEditConfig processEditConfigDB = ProcessEditConfigRepo.findByProcessTypeAndNodeTypeAndActivated(processType, nodeType, DictConstant.ACTIVATED_YES);
        Assert.notNull(processEditConfigDB, "要删除流程节点可编辑字段表设置信息，根据类型和节点查询不存在");

        //设置删除状态及操作人员
        processEditConfigDB.setActivated(DictConstant.ACTIVATED_NO);
//        RequestTool.fillOpInfo(processEditConfigDB, DictConstant.OP_SOURCE_WEB, DictConstant.OP_ACTION_DELETE);

        return ProcessEditConfigRepo.save(processEditConfigDB);
    }
}
